package com.pl.redis;

import com.google.common.collect.Iterators;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * ----------------------------------------------------------------------------
 * Date            Author           Version        Comments
 * 2022/1/27 11:38  pengli         1.0       Initial Version
 */

@Slf4j
@Component("commonRedisUtil")
public class RedisUtil {
    @SuppressWarnings("rawtypes")
    @Autowired
    @Qualifier("commonRedisTemplate")
    private RedisTemplate redisTemplate;

    @Value("${cmsr.redis.multiSetSize:1000}")
    private Integer multiSetSize;

    @Value("${cmsr.redis.multiGetSize:500}")
    private Integer multiGetSize;

    /**
     * 批量删除对应的value
     *
     * @param keys
     */
    public void remove(final String... keys) {
        for (String key : keys) {
            remove(key);
        }
    }

    public void expire(final String key, long time) {
        if (exists(key)) {
            redisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
    }

    /**
     * 批量删除key
     *
     * @param pattern
     */
    @SuppressWarnings("unchecked")
    public void removePattern(final String pattern) {
        Set<Serializable> keys = redisTemplate.keys(pattern);
        if (keys.size() > 0)
            redisTemplate.delete(keys);
    }

    public void removeWithPrefix(final String prefix, final String key) {
        remove(prefix + key);
    }

    /**
     * 删除对应的value
     *
     * @param key
     */
    @SuppressWarnings("unchecked")
    public void remove(final String key) {
        if (exists(key)) {
            redisTemplate.delete(key);
        }
    }

    @SuppressWarnings("unchecked")
    public boolean exists(final String prefix, final String key) {
        return redisTemplate.hasKey(prefix + key);
    }

    /**
     * 判断缓存中是否有对应的value
     *
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public boolean exists(final String key) {
        return redisTemplate.hasKey(key);
    }

    public Object getWithPrefix(final String prefix, final String key) {
        return get(prefix + key);
    }

    public Object getWithPrefix(final String prefix, Object... keys) {
        String keyStr = "";
        for (Object key : keys) {
            keyStr += key.toString() + RedisKeys.KEY_SEPARATOR;
        }
        // 去掉最后一个 _
        keyStr = keyStr.substring(0, keyStr.length() - 1);
        return get(prefix + keyStr);
    }

    /**
     * 读取缓存
     *
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public Object get(final String key) {
        Object result = null;
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        return operations.get(key);
    }

    public boolean setWithPrefix(final String prefix, final String key, Object value) {
        return set(prefix + key, value);
    }

    /**
     * 写入缓存
     *
     * @param key
     * @param value
     * @return
     */
    @SuppressWarnings("unchecked")
    public boolean set(final String key, Object value) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (Exception e) {
            log.warn("set redis key error", e);
        }
        return result;
    }

    public boolean setWithPrefixWithExpire(final String prefix, final String key, Object value, Long expireTime) {
        return set(prefix + key, value, expireTime);
    }

    /**
     * 更新value而不改变生命周期
     */
    public boolean setWithPrefixKeepExpire(final String prefix, final String key, Object value){
        long expireTime = ttl(prefix + key);
        return set(prefix + key, value, expireTime);
    }

    public boolean setWithPrefixWithExpire(Object value, Long expireTime, final String prefix, Object... keys) {
        String keyStr = "";
        for (Object key : keys) {
            keyStr += key.toString() + RedisKeys.KEY_SEPARATOR;
        }
        // 去掉最后一个 _
        if (keyStr.length() > 0) {
            keyStr = keyStr.substring(0, keyStr.length() - 1);
        }
        return set(prefix + keyStr, value, expireTime);
    }

    /**
     * 写入缓存
     *
     * @param key
     * @param value
     * @return
     */
    @SuppressWarnings("unchecked")
    public boolean set(final String key, Object value, Long expireTime) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value, expireTime, TimeUnit.SECONDS);
//            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            result = true;
        } catch (Exception e) {
            log.warn("set redis key error", e);
        }
        return result;
    }

    /**
     * delete the object stored in redis
     *
     * @param prefix
     * @param key
     * @return
     */
    public boolean deleteWithPrefix(final String prefix, final String key) {
        return delete(prefix + key);
    }

    public boolean deleteWithPrefix(final String prefix, Object... keys) {
        String keyStr = "";
        for (Object key : keys) {
            keyStr += key.toString() + RedisKeys.KEY_SEPARATOR;
        }
        // 去掉最后一个 _
        keyStr = keyStr.substring(0, keyStr.length() - 1);

        return delete(prefix + keyStr);
    }


    public boolean delete(final String key) {
        boolean result = true;
        try {
            redisTemplate.delete(key);
        } catch (Exception e) {
            result = false;
            log.warn("delete redis key error", e);
        }
        return result;
    }

    public boolean delete(final Object[] keys) {
        boolean result = true;
        try {
            redisTemplate.delete(keys);
        } catch (Exception e) {
            result = false;
            log.warn("delete redis key error", e);
        }
        return result;
    }

    public Long increament(String key) {
        return redisTemplate.opsForValue().increment(key, 1);
    }

    public Long increament(String key, long val) {
        return redisTemplate.opsForValue().increment(key, val);
    }

    public Long decrement(String key, long val) {
        return redisTemplate.opsForValue().decrement(key, val);
    }

    public Long incrementMapValue(String key, String hashKey) {
        return redisTemplate.opsForHash().increment(key, hashKey, 1);
    }

    public void setAllObjectMapValue(String key, Map map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    public void setObjectMapValue(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    public Object getObjectMapValue(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }

    public Long deleteMapValue(String key, String... hashKey) {
        return redisTemplate.opsForHash().delete(key, hashKey);
    }

    public boolean existHashKey(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey) != null;
    }

    /**
     * 获取对象的TTL（生存时间）配置
     *
     * @param key
     * @return ttl, 单位秒，如果没设置，返回-1
     */
    public long ttl(String key) {
        return redisTemplate.getExpire(key);
    }


    /**
     * 更新缓存的存活周期，单位秒
     *
     * @param key
     * @param ttl
     */
    public void refreshTtl(String key, long ttl) {
        redisTemplate.expire(key, ttl, TimeUnit.SECONDS);
    }

    /**
     * 获得分布式锁
     *
     * @param key 分布式锁的 键值
     * @param ttl 分布式锁的有效期，防止锁不释放
     * @return 是否成功获得锁
     */
    public Boolean lock(String key, long ttl) {
        return redisTemplate.opsForValue().setIfAbsent(key, true, ttl, TimeUnit.SECONDS);
    }

    /**
     * 获得整个redis Hash Map
     *
     * @param key
     * @return
     */
    public Map<String, Object> getHash(String key) {
        return getHash(key, "*");
    }

    public long getHashLen(String key) {
        Long len = redisTemplate.opsForHash().size(key);
        return len != null ? len : 0;
    }

    /**
     * 获得　hash-key匹配pattern的 redis Hash Map
     *
     * @param key
     * @param pattern
     * @return
     */
    public Map<String, Object> getHash(String key, String pattern) {

        if (pattern == null || pattern.equals("")) {
            pattern = "*";
        }
        Map<String, Object> entries = new HashMap<>();
        HashOperations<String, String, Object> hashOper = redisTemplate.opsForHash();

        ScanOptions.ScanOptionsBuilder builder = new ScanOptions.ScanOptionsBuilder();
        ScanOptions options = builder.count(1000).match(pattern).build();

        Cursor<Map.Entry<String, Object>> cursor = hashOper.scan(key, options);
        while (cursor.hasNext()) {
            Map.Entry<String, Object> next = cursor.next();
            entries.put(next.getKey(), next.getValue());
        }

        return entries;
    }

    public Set<String> keys(String keyPattern) {
        return redisTemplate.keys(keyPattern);
    }

    public List<Object> multiGet(Collection<String> keys) {
        // 100 分片，效率最高
        List<Object> values = new ArrayList<>();
        Iterators.partition(keys.iterator(), multiGetSize).forEachRemaining(
                sliceKeys -> {
                    values.addAll(redisTemplate.opsForValue().multiGet(sliceKeys));
                }
        );

        return values;
    }

    public void multiSet(Map modelMap) {
        ValueOperations valueOperations = redisTemplate.opsForValue();

        Iterators.partition(modelMap.keySet().iterator(), multiSetSize).forEachRemaining(
                sliceKeys -> {
                    List keyList = (List) sliceKeys;
                    Map sliceMap = new HashMap();
                    for (Object key : keyList) {
                        sliceMap.put(key, modelMap.get(key));
                    }

                    valueOperations.multiSet(sliceMap);
                });
    }

    public void multiDelete(Collection<String> keys) {
        Iterators.partition(keys.iterator(), 5000)
                .forEachRemaining(sliceKeys -> redisTemplate.delete(sliceKeys));
    }

//    public void multiSet(Map modelMap, int splitSize) {
//        ValueOperations valueOperations = redisTemplate.opsForValue();
//
//        Iterators.partition(modelMap.keySet().iterator(), splitSize).forEachRemaining(
//                sliceKeys -> {
//                    List keyList = (List) sliceKeys;
//                    Map sliceMap = new HashMap();
//                    for (Object key : keyList) {
//                        sliceMap.put(key, modelMap.get(key));
//                    }
//
//                    valueOperations.multiSet(sliceMap);
//                });
//    }
//
//    public List<Object> multiGet(Collection<String> keys, int splitSize) {
//        // 100 分片，效率最高
//        List<Object> values = new ArrayList<>();
//        Iterators.partition(keys.iterator(), splitSize).forEachRemaining(
//                sliceKeys -> {
//                    values.addAll(redisTemplate.opsForValue().multiGet(sliceKeys));
//                }
//        );
//
//        return values;
//    }

}